﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using LDriver.Protocols;

namespace LDriver
{
    public class CommunicationManager
    {

        private ICommunicationProvider localprovider;
        private ICommunicationProtocol localProtocol;
        public List<Packet> RecivedPackets { get; set; }
        public delegate void DataRecivedEventHandler(object sender, EventArgs e);

        private Command SendedCommand;
        public List<Byte> Buffer;

        // Questo è il backgroundworker che viene eseguito sull'inizializzazione
        // del communication manager e estrae le informazioni dal buffer
        BackgroundWorker bwAnalyzeBuffer = new BackgroundWorker();


        private bool completed = false;
        private bool canceled = false;

        //public List<LidarSurvey> Surveys
        //{
        //    get
        //    {
        //        //return localProtocol.Surveys.Cast<LidarSurvey>().ToList();

        //        List<LidarSurvey> ret = new List<LidarSurvey>();

        //        foreach (ISurvey survey in localProtocol.Surveys)
        //            ret.Add((LidarSurvey)survey);

        //        return ret;


        //    }
        //}
        public LidarSurvey FirstSurvey
        {
            get
            {
                //return localProtocol.Surveys.Cast<LidarSurvey>().ToList();


                /*List<LidarSurvey> ret = new List<LidarSurvey>();

                foreach (ISurvey survey in localProtocol.Surveys)
                    ret.Add((LidarSurvey)survey);
                */
                if (localProtocol.Surveys.Count > 0) {
                    return (LidarSurvey)localProtocol.Surveys[0];

                }
                return null;

            }

        }

        public void RemoveSurvey(ISurvey s)
        {
                localProtocol.Surveys.Remove(s);

        }


        public CommunicationManager(ICommunicationProvider provider, ICommunicationProtocol protocol)
        {
            
            localprovider = provider;
            localprovider.DataReceived += AddDataToBuffer;

            localProtocol = protocol;

            // Inizializzo il Buffer 
            Buffer = new List<byte>();

            // Inizializzo il thread che si mette in lettura del Buffer per pacchettizzare
            bwAnalyzeBuffer.WorkerSupportsCancellation = true;
            bwAnalyzeBuffer.WorkerReportsProgress = false;
            bwAnalyzeBuffer.DoWork += bwAnalyzeBuffer_DoWork;
            bwAnalyzeBuffer.RunWorkerCompleted += bwAnalyzeBuffer_RunWorkerCompleted;
            

        }

        private void bwAnalyzeBuffer_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
                canceled = true;
            else
                completed = true;
        }


        public Command GetCommand(string commandName)
        {
            // Recupero il Comando chiamando il protocollo
            SendedCommand = localProtocol.GetCommand(commandName);
            return SendedCommand;
        }

        public void SendCommand(Command data)
        {

            try
            {
                // L'invio di un nuovo comando interrompe qualsiasi attività sul buffer
                // e reinizializza il buffer 
                if (bwAnalyzeBuffer.WorkerSupportsCancellation)
                {
                    bwAnalyzeBuffer.CancelAsync();
                    localprovider.Close();



                }

                Buffer = new List<byte>();

                // Attivo il background worker che lavora sul buffer
                if (bwAnalyzeBuffer.IsBusy != true) bwAnalyzeBuffer.RunWorkerAsync();

                // Invio il comando recuperato tramite il GetCommand
                localprovider.SendData(data);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }


        private void bwAnalyzeBuffer_DoWork(object sender, DoWorkEventArgs e)
        {

            while (!e.Cancel)
            {
                if (bwAnalyzeBuffer.CancellationPending)
                { 
                    e.Cancel = true;
                    Buffer.Clear();
                }
                
                if (Buffer.Count > SendedCommand.ResponseHeader.Length)
                    AnalyzeBuffer();
            }
        }

        private void AddDataToBuffer(object sender, byte[] IncomingData)
        {
            // Aggiungo i byte in arrivo al buffer di byte non ancora gestiti
            if (IncomingData.Length>0)
            {
                Buffer.AddRange(IncomingData);
            } 
        }

        private void AnalyzeBuffer()
        {
            try
            {
            //// All'interno del protocollo eseguirò tutte le operazioni sui byte in arrivo.

            //// A seconda del Command inviato mi aspetto una risposta strutturata secondo
            //// le informazioni presenti nel Packet.
            byte[] ResponseHeader = SendedCommand.ResponseHeader;

            // Per prima cosa recupero le informazioni del pacchetto che mi aspetto dal comando inviato
            // cercando l'header all'interno del buffer
            int ix = IndexOf(ResponseHeader, 0);

            // Il buffer contiene l'header della risposta ad una determinata posizione.
            // Effettuo l'estrazione del contenuto e poi cancello le informazioni
            // appena estratte
            int ResponseLenght = SendedCommand.ResponseLenght;
            while (ix + ResponseHeader.Length  + ResponseLenght <= Buffer.Count)
            {
                // Creo un nuovo pacchetto da passare al protocollo
                Packet PacketToAnalyze = new Packet(ResponseHeader, ResponseLenght);
                PacketToAnalyze.SetContent(Buffer.GetRange(ix + ResponseHeader.Length, ResponseLenght));

                // Eseguo la procedura del protocollo dedicata alla gestione dei pacchetti di risposta
                if (localProtocol.AnalyzePacket(SendedCommand, PacketToAnalyze))
                {
                    // Rimpicciolisco il buffer
                    Buffer.RemoveRange(ix + ResponseHeader.Length, ResponseLenght);

                }
                else
                {
                    // Si è verificato qualche problema nella gestione dei dati provenienti
                    // dal buffer, non cancello tutto il buffer, ma solamente il primo
                    // byte, di modo da controllare se le informazioni errate arrivano da
                    // perdite di byte
                    //Buffer.RemoveRange(0, 1);
                }

            }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        // Cerca un pattern di byte[] all'interno del buffer
        private int IndexOf(byte[] pattern, int offset = 0)
        {
            if (pattern.Length == 0) return -1;

            byte firstByte = pattern[0];
            int index = -1;

            if ((index = Buffer.IndexOf(firstByte, offset)) >= 0)
            {
                for (int i = 0; i < pattern.Length; i++)
                {
                    if (index + i >= Buffer.Count ||
                     pattern[i] != Buffer[index + i]) return -1;
                }
            }

            return index;

        }

        public void CancelWorker()
        {
           bwAnalyzeBuffer.CancelAsync();
        }
    }

}
